ASM 汇编语言 8

  • Created on 2014-11

教材:《汇编语言》(第二版)王爽 著 清华大学出版社

章十一、标志寄存器

CPU内部有一种特殊的寄存器,具有以下3个作用:
(1)存储相关指令的某些执行结果;
(2)为CPU执行相关指令提供行为依据;
(3)控制CPU的相关工作方式。

flag:标志寄存器,16位,按位起作用。
15 14 13 12 11  10   9   8    7    6    5   4   3   2   1   0
                  OF  DF  IF  TF  SF  ZF       AF      PF      CF
空出的都是在8086CPU中没有被使用,不具有含义。

11.1 ZF 标志
Zero 零标志位:记录相关指令执行后,其结果是否为0。
结果为0,则 zf = 1 ;若结果不为0,则 zf = 0

注意:8086CPU的指令集中,
有的指令的执行是影响标志寄存器的,它们大都是运算指令(逻辑或算术运算)。
有的则没有影响,如mov、push、pop等,它们大都是传送指令

11.2 PF 标志
Parity 奇偶标志位:记录相关指令执行后,其结果的所有bit位中1的个数是否为偶数。
1的个数为偶数,则 pf = 1 ;若为奇数pf = 0 

11.3 SF 标志
Sign 符号标志位:记录相关指令执行后,其结果是否为负。
结果为负,则 SF = 1 ;若结果为正,则 SF = 0

11.4 CF 标志
Carry 进位标志位:一般情况下,在无符号数运算时,
记录运算结果的最高有效位向更高位的进位值(add),或从更高位的借位值(sub)。

11.5 OF 标志
Overflow 溢出标志:在进行有符号数运算时,若结果超过了及其所能表示的范围称为溢出。
发生溢出,则 OF = 1 ;若没有,则 OF = 0

CF 和 OF 的区别:
CF对无符号数运算有意义的标志位;
OF是对有符号数(同上)……

例子:
          mov al, 98     add al, 99          ;即98+99
对于无符号数(8bits,0~255)没有进位,CF = 0 ;
对于有符号数(8bits,-128~127)有溢出,OF = 1 。
          mov al, 0F0H     add al, 88H
对于unsigned number,即是 240 + 136 ,有进位,CF = 1 ;
对于signed,有溢出,即是 -16 + (-120),OF = 1 。
          mov al, 0F0H     add al, 78H
对于unsigned number,即是 240 + 120 ,有进位,CF = 1 ;
对于signed,有溢出,即是 -16 + 120 ,OF = 0 。

怎么查看标志寄存器请看见本文文末的11.12节


11.6 adc 指令:带进位加法指令,利用CF位上记录的进位值。
指令格式:同add。
功能:operand1 = operand1 + operand2 + CF

adc 指令的用途解析:
加法分两步来进行:
(1)低位相加;
(2)高位相加再加上低位相加产生的进位值。
例子: 1EF000H + 201000H
因为操作数都超过了16位,无法将它完整放到一个16位大小的寄存器中,
无法通过add一次得到结果,所以——
mov ax, 001EH     ;ax放高16位
mov bx, F000H     ;bx放低16位
add  bx, 1000H     ;低位相加
adc  ax, 0020H     ;高位相加,再加CF(即低位的进位值)

11.7 sbb 指令:带借位减法指令,利用CF位上记录的借位值。
borrow借位。
指令格式:同sub。
功能:operand1 = operand1 - operand2 - CF
其用途和意义类似于adc!

11.8 cmp 指令:比较指令。
功能相当于减法指令,只是不保存结果。
cmp指令执行后,根据计算结果来设置标志寄存器(主要是 zf cf )。
格式:cmp oper1, oper2

例子:cmp ax, bx
|情况              |(ax)-(bx)的结果   |标志位情况
(ax) == (bx)           = 0                zf = 1
(ax) != (bx)            != 0               zf != 0
(ax) < (bx)             < 0                cf = 1
(ax) <= (bx)           <= 0              cf = 1 or zf = 1
(ax) > (bx)             > 0                cf = 0 and zf = 0
(ax) >= (bx)           >= 0              cf = 0

为什么判断(ax)和(bx)的大小关系,看的是 cf 标志位,而不是 sf 呢?
因为一般情况下 (ax) - (bx) < 0,即是(ax) < (bx),sf = 1 ;
但是当涉及有符号数的对比时,如34 - (-96) = 130 发生了溢出,
结果82H,即-126!sf = 1,但是 (ax) > (bx)。

不过 sf 结合 of 还是可以判断(ax)和(bx)的大小关系的。
sf = 0 and of = 0 —— (ax) >= (bx) 
sf = 1 and of = 0 —— (ax) <= (bx)
sf = 0 and of = 1 —— (ax) <= (bx)
sf = 1 and of = 1 —— (ax) >= (bx) 
如果溢出,逻辑上的真正结果必然和实际结果“相反


11.9 检测比较结果的条件转移指令
条件转移指令通常和cmp相配合使用。
指令      含义                    检测相关标志位
je          == 等于则转移      zf = 1
jne        != 不等于则..         zf = 0
jb          < 低于则..             cf = 1
jnb        >= 不低于..           cf = 0
ja          > 高于..                cf = 0 and zf = 0
jna        <= 不高于..           cf = 1 or zf = 0

e —— equal
b —— below
a —— above

在使用这些跳转指令前,先用不用cmp指令,由我们决定。
例子:
编程统计data段中,数值大于8的字节的个数,用ax保存统计结果。
assume cs:code, ds:data
data segment
        db 0, 2, 1, 4, 7, 3, 5, 9, 6, 8, 10, 12, 15, 14, 13, 11
data ends
code segment
start:  mov ax, data
        mov ds, ax
        mov bx, 0
        
        mov ax, 0
        mov cx, 16

s:      cmp byte ptr [bx], 8
        jna next
        inc ax
next:   inc bx
        loop s
        
        mov ax, 4c00h
        int 21h
code ends
end start


11.10 DF 标识 和 串传送指令
DF :方向标志位
它在串处理指令中,控制每次操作后si、di是增是减。
串传送指令: movsb、movsw —— moving string byte / word

movsb (无参数)
功能:相当于以下几步操作。
(1)((es) * 16 + (di)) = ((ds) * 16 + (si))
(2)若df = 0,则 (si) = (si) + 1
                  (di) = (di) + 1
     若df = 1,则 (si) = (si) - 1
                  (di) = (di) - 1
 用汇编语言描述则是:
mov es:[di], byte ptr ds:[si]     ;8086CPU并不支持这样的指令,这里仅仅为描述
if df = 0     inc si     inc di
if df = 1     dec si     dec di

movsw (无参数)
功能 用汇编语言描述则是:
mov es:[di], word ptr ds:[si]     ;8086CPU并不支持这样的指令,这里仅仅为描述
if df = 0     add si, 2     add di, 2
if df = 1     sub si, 2     sub di, 2


一般 movsb / movsw 都和 rep 配合使用:
rep movsb
rep movsw
用汇编描述就是:
s:   movsb
     loop s
s:   movsw
     loop s

rep 指令的作用:
根据cx的值,重复执行后面的串传送指令。

然后可以根据flag寄存器的 df 标志,决定传送方向:
df = 0     从前向后 传送
df = 1     相反

cld 指令 —— clear df   标志寄存器的df置为0
std 指令 —— set df     :将标志寄存器的df位,置为1


原书P233(2)编程:
用串传送指令,将F000H段的最后16个字符复制到data段中。
assume cs:code
data segment
                db 16 dup (0)
data ends

code segment
start:       mov ax, 0f000h
                                mov ds, ax
                                mov si, 0ffffh
                                
                                mov ax, data
                                mov es, ax
                                mov di, 15

                                std
                                mov cx, 16
                                rep movsb

                                mov ax, 4c00h
                                int 21h
code ends
end start


11.11 pushfpopf

pushf 指令:将标志寄存器的值压栈。
popf  指令:从栈中弹出数据,送入标志寄存器中。



11.12 标志寄存器在Debug中的表示

用debug.exe查看当前标志寄存器的标志位值?
用R指令,得到的信息右下角: NV   UP   EI   PL   NZ   NA   PO   NC
这些符号代表的就是标志寄存器里常用标志位的值。

标志位寄存器,符号值对应表

标志                                                                 值为1的标记          值为0的标记

溢出标志OF(Over flow flag)               OV  overflow       NV
方向标志DF(Direction flag)                DN
  down           UP

中断标志IF(Interrupt flag)                 EI
                      DI
符号标志SF(Sign flag)                        NG
  negative       PL positive

零标志ZF(Zero flag)                           ZR
  zero             NZ not zero
辅助标志AF(Auxiliary carry flag)       AC
  assist           NA

奇偶标志PF(Parity flag)                     PE
 even              PO odd
进位标志CF(Carry flag)                      CY
carry              NC not carry



实验11 编写子程序
名称:letterc
功能:将以0结尾的字符串中的小写字母转变成大写字母。
参数:ds:si指向字符串首地址。
源码:

assume cs:code, ds:data

data segment
     db "Beginner's All-purpose Symbolic Instruction Code.", 0
data ends

code segment

;letterc:     将以 0 结尾的字符串中的小写字母转换成大写
;参数:ds:di 指向字符串首地址
;返回:无
letterc:
     ;暂存寄存器
     pushf
     push ax
    
     mov ah, 0     ;!!!错误,jcxz测试的是cx是否为0!
    
     r0:
          mov al, ds:[di]     ;!!!错误,jcxz测试的是cx是否为0!
          jcxz ok
         
          ;ascii: 61h - a, 7ah - z
          cmp al, 61h
          jb next
          cmp al, 7ah
          ja next
         
          and byte ptr ds:[di], 11011111b
         
     next:
          inc di
          jmp r0
     ok:
    
     ;恢复寄存器
     popf
     pop ax
    
     ret

start:
     mov ax, data
     mov ds, ax
     mov si, 0
    
     call letterc
    
     mov ax, 4c00h
     int 21h
    
code ends

end start

实验asm中有错:
;jcxz测试的是cx是否为0!
;实验代码中,ah/al应改为ch/cl!
0%